package com.jl15988.tree;

import com.jl15988.tree.exception.TreeUtilException;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 树工具
 *
 * @author Jalon
 * @since 2023/11/2 14:24
 **/
@Data
public class TreeUtils<T> {

    /**
     * ID字段名
     */
    private String idField;

    /**
     * 父级ID字段名
     */
    private String parentIdField;

    /**
     * 子集字段名
     */
    private String childField;

    /**
     * ID字段
     */
    private XFunction<T, ?> idFun;

    /**
     * 父级ID字段
     */
    private XFunction<T, ?> parentIdFun;

    /**
     * 子集字段
     */
    private XFunction<T, List<T>> childFun;

    /**
     * 数据列表
     */
    private List<T> objs;

    public TreeUtils() {

    }

    public TreeUtils(String idField, String parentIdField) {
        this.idField = idField;
        this.parentIdField = parentIdField;
    }

    public TreeUtils(String idField, String parentIdField, String childField) {
        this.idField = idField;
        this.parentIdField = parentIdField;
        this.childField = childField;
    }

    public TreeUtils(XFunction<T, ?> id, XFunction<T, ?> parentId) {
        this.idFun = id;
        this.parentIdFun = parentId;
    }

    public TreeUtils(XFunction<T, ?> id, XFunction<T, ?> parentId, XFunction<T, List<T>> child) {
        this.idFun = id;
        this.parentIdFun = parentId;
        this.childFun = child;
    }

    /**
     * 过滤掉没有子集的数据
     */
    public List<T> filterNoChild() {
        return this.objs.stream().filter(t -> {
            List<T> childValue = getChildValue(t);
            return Objects.nonNull(childValue) && !childValue.isEmpty();
        }).collect(Collectors.toList());
    }

    /**
     * 构建树形结构
     *
     * @param objs 数据列表
     */
    public TreeUtils<T> buildTree(List<T> objs) {
        buildTree(objs, null, null);
        return this;
    }

    /**
     * 构建树形结构
     *
     * @param objs   数据列表
     * @param mapper 节点处理器
     */
    public TreeUtils<T> buildTree(List<T> objs, Function<MapperParam, T> mapper) {
        buildTree(objs, mapper, null);
        return this;
    }

    /**
     * 构建树形结构
     *
     * @param objs       数据列表
     * @param leafMapper 叶子节点处理器
     */
    public TreeUtils<T> buildTreeLeaf(List<T> objs, Function<T, T> leafMapper) {
        buildTree(objs, null, leafMapper);
        return this;
    }

    /**
     * 构建树形结构
     *
     * @param objs       数据列表
     * @param mapper     节点处理器
     * @param leafMapper 叶子节点处理器
     */
    public TreeUtils<T> buildTree(List<T> objs, Function<MapperParam, T> mapper, Function<T, T> leafMapper) {
        for (T obj : objs) {
            toBuild(objs, obj, mapper, leafMapper);
        }
        this.objs = objs;
        return this;
    }

    /**
     * 根据指定根节点 ID 构建树形结构数据
     *
     * @param objs        数据列表
     * @param rootIdValue 指定根节点 ID
     */
    public <R> void buildTreeWithRoot(List<T> objs, R rootIdValue) {
        buildTreeWithRoot(objs, rootIdValue, null, null);
    }

    /**
     * 根据指定根节点 ID 构建树形结构数据
     *
     * @param objs        数据列表
     * @param rootIdValue 指定根节点 ID
     * @param mapper      节点处理器
     */
    public <R> void buildTreeWithRoot(List<T> objs, R rootIdValue, Function<MapperParam, T> mapper) {
        buildTreeWithRoot(objs, rootIdValue, mapper, null);
    }

    /**
     * 根据指定根节点 ID 构建树形结构数据
     *
     * @param objs        数据列表
     * @param rootIdValue 指定根节点 ID
     * @param leafMapper  叶子节点处理器
     */
    public <R> void buildTreeWithRootLeaf(List<T> objs, R rootIdValue, Function<T, T> leafMapper) {
        buildTreeWithRoot(objs, rootIdValue, null, leafMapper);
    }

    /**
     * 根据指定根节点 ID 构建树形结构数据
     *
     * @param objs        数据列表
     * @param rootIdValue 指定根节点 ID
     * @param mapper      节点处理器
     * @param leafMapper  叶子节点处理器
     */
    public <R> void buildTreeWithRoot(List<T> objs, R rootIdValue, Function<MapperParam, T> mapper, Function<T, T> leafMapper) {
        T obj = getItemByIdValue(rootIdValue);
        if (Objects.isNull(obj)) {
            return;
        }
        toBuild(objs, obj, mapper, leafMapper);
    }

    /**
     * 根据指定根节点构建树形结构数据
     *
     * @param objs     数据列表
     * @param rootItem 指定根节点
     */
    public <R> void buildTreeWithRoot(List<T> objs, Function<List<T>, T> rootItem) {
        buildTreeWithRoot(objs, rootItem, null, null);
    }

    /**
     * 根据指定根节点构建树形结构数据
     *
     * @param objs     数据列表
     * @param rootItem 指定根节点
     * @param mapper   节点处理器
     */
    public <R> void buildTreeWithRoot(List<T> objs, Function<List<T>, T> rootItem, Function<MapperParam, T> mapper) {
        buildTreeWithRoot(objs, rootItem, mapper, null);
    }

    /**
     * 根据指定根节点构建树形结构数据
     *
     * @param objs       数据列表
     * @param rootItem   指定根节点
     * @param leafMapper 叶子节点处理器
     */
    public <R> void buildTreeWithRootLeaf(List<T> objs, Function<List<T>, T> rootItem, Function<T, T> leafMapper) {
        buildTreeWithRoot(objs, rootItem, null, leafMapper);
    }

    /**
     * 根据指定根节点构建树形结构数据
     *
     * @param objs       数据列表
     * @param rootItem   指定根节点
     * @param mapper     节点处理器
     * @param leafMapper 叶子节点处理器
     */
    public void buildTreeWithRoot(List<T> objs, Function<List<T>, T> rootItem, Function<MapperParam, T> mapper, Function<T, T> leafMapper) {
        T obj = rootItem.apply(objs);
        toBuild(objs, obj, mapper, leafMapper);
    }

    /**
     * 递归赋值
     */
    public void toBuild(List<T> objs, T obj, Function<MapperParam, T> mapper, Function<T, T> leafMapper) {
        List<T> childrenList = new ArrayList<>();
        for (T t : objs) {
            Object value = getParentIdValue(t);
            Object value2 = getIdValue(obj);

            if (Objects.nonNull(value) && value.equals(value2)) {
                childrenList.add(t);
            }
        }
        if (!childrenList.isEmpty()) {
            setChildValue(obj, childrenList);

            for (T children : childrenList) {
                if (Objects.nonNull(mapper)) {
                    toBuild(objs, mapper.apply(new MapperParam(children, obj)), mapper, leafMapper);
                } else {
                    toBuild(objs, children, mapper, leafMapper);
                }
            }
        } else {
            if (Objects.nonNull(leafMapper)) {
                leafMapper.apply(obj);
            }
        }
    }

    /**
     * 将树形结构转为结合列表
     *
     * @param objs 树形列表数据
     */
    public List<T> toALineList(List<T> objs) {
        List<T> newObjs = new ArrayList<>();
        toDoALineList(objs, newObjs);
        return newObjs;
    }

    private void toDoALineList(List<T> objs, List<T> newObjs) {
        for (T obj : objs) {
            newObjs.add(obj);
            List<T> childList = getChildValue(obj);
            if (childList != null && !childList.isEmpty()) {
                toDoALineList(childList, newObjs);
            }
        }
    }

    /**
     * 根据指定根节点 ID 获取其下的所有后代节点
     *
     * @param objs        数据列表
     * @param rootIdValue 指定根节点 ID
     */
    public <R> List<T> getBranchList(List<T> objs, R rootIdValue) {
        List<T> result = new ArrayList<>();
        buildTreeWithRoot(objs, rootIdValue, mapperParam -> {
            result.add(mapperParam.getCurrent());
            return mapperParam.getCurrent();
        });
        return result;
    }

    /**
     * 根据指定根节点获取其下的所有后代节点
     *
     * @param objs     数据列表
     * @param rootItem 根节点
     */
    public List<T> getBranchList(List<T> objs, Function<List<T>, T> rootItem) {
        List<T> result = new ArrayList<>();
        buildTreeWithRoot(objs, rootItem, mapperParam -> {
            result.add(mapperParam.getCurrent());
            return mapperParam.getCurrent();
        });
        return result;
    }

    /**
     * 根据指定节点 ID 获取节点
     *
     * @param idValue 指定节点 ID
     */
    public <R> T getItemByIdValue(R idValue) {
        return objs.stream().filter(item -> idValue.equals(getIdValue(item))).findFirst().orElse(null);
    }

    /**
     * 获取字段内容
     */
    private Object getFieldValue(T obj, String field) {
        try {
            Class<?> clazz = obj.getClass();
            Field _field = clazz.getDeclaredField(field);
            _field.setAccessible(true);
            return _field.get(obj);
        } catch (Exception e) {
            throw new TreeUtilException(e);
        }
    }

    /**
     * 赋值字段内容
     */
    private static <T, S> void setFieldValue(T obj, String field, S value) throws NoSuchFieldException, IllegalAccessException {
        Class<?> clazz = obj.getClass();
        Field _field = clazz.getDeclaredField(field);
        _field.setAccessible(true);
        _field.set(obj, value);
    }

    private Object getIdValue(T obj) {
        if (Objects.isNull(obj)) return null;
        if (Objects.isNull(idFun) && Objects.isNull(idField)) {
            throw new TreeUtilException("id field not set");
        }
        if (Objects.nonNull(idFun)) {
            return idFun.apply(obj);
        }
        return getField(obj, this.idField);
    }

    private Object getParentIdValue(T obj) {
        if (Objects.isNull(obj)) return null;
        if (Objects.isNull(parentIdFun) && Objects.isNull(parentIdField)) {
            throw new TreeUtilException("parentId field not set");
        }
        if (Objects.nonNull(parentIdFun)) {
            return parentIdFun.apply(obj);
        }
        return getField(obj, this.parentIdField);
    }

    private List<T> getChildValue(T obj) {
        if (Objects.isNull(obj)) return null;
        if (Objects.isNull(childFun) && Objects.isNull(childField)) {
            return null;
        }
        if (Objects.nonNull(childFun)) {
            return childFun.apply(obj);
        }
        Object childValue = getField(obj, this.childField);
        if (Objects.isNull(childValue)) return null;
        return (List<T>) childValue;
    }

    private void setIdValue(T obj, Object value) throws IllegalAccessException {
        if (Objects.isNull(obj)) return;
        if (Objects.isNull(idFun) && Objects.isNull(idField)) {
            throw new TreeUtilException("id field not set");
        }
        String fieldName = this.idField;
        if (Objects.nonNull(idFun)) {
            fieldName = getFieldName(idFun);
        }
        setField(obj, fieldName, value);
    }

    private void setParentIdValue(T obj, Object value) throws IllegalAccessException {
        if (Objects.isNull(obj)) return;
        if (Objects.isNull(parentIdFun) && Objects.isNull(parentIdField)) {
            throw new TreeUtilException("parentId field not set");
        }
        String fieldName = this.parentIdField;
        if (Objects.nonNull(parentIdFun)) {
            fieldName = getFieldName(parentIdFun);
        }
        setField(obj, fieldName, value);
    }

    private void setChildValue(T obj, Object value) {
        if (Objects.isNull(obj)) return;
        if (Objects.isNull(childFun) && Objects.isNull(childField)) {
            return;
        }
        String fieldName = this.childField;
        if (Objects.nonNull(childFun)) {
            fieldName = getFieldName(childFun);
        }
        setField(obj, fieldName, value);
    }

    /**
     * 获取属性值
     *
     * @param obj 类
     * @return 值
     */
    private Object getField(T obj, String fieldName) {
        try {
            Class<?> clazz = obj.getClass();
            List<Field> fieldList = getFieldByCurrentAndSuper(clazz);
            if (!fieldList.isEmpty()) {
                for (Field field : fieldList) {
                    if (field.getName().equals(fieldName)) {
                        // 设置可访问私有属性
                        field.setAccessible(true);
                        return field.get(obj);
                    }
                }
            }
            return null;
        } catch (Exception e) {
            throw new TreeUtilException(e.getMessage());
        }
    }

    /**
     * 赋值属性
     */
    private static <T, S> void setField(T obj, String fieldName, S value) {
        try {
            Class<?> clazz = obj.getClass();
            List<Field> fieldList = getFieldByCurrentAndSuper(clazz);
            if (!fieldList.isEmpty()) {
                for (Field field : fieldList) {
                    if (field.getName().equals(fieldName)) {
                        // 设置可访问私有属性
                        field.setAccessible(true);
                        field.set(obj, value);
                    }
                }
            }
        } catch (Exception e) {
            throw new TreeUtilException(e.getMessage());
        }
    }

    /**
     * 从当前以及父类中获取全部字段
     *
     * @param clazz 属性所在类
     */
    public static List<Field> getFieldByCurrentAndSuper(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        getFieldByCurrentAndSuper(clazz, fields);
        return fields;
    }

    /**
     * 从当前以及父类中获取全部字段
     *
     * @param clazz  属性所在类
     * @param fields 属性集合
     */
    private static List<Field> getFieldByCurrentAndSuper(Class<?> clazz, List<Field> fields) {
        Field[] declaredFields = clazz.getDeclaredFields();
        fields.addAll(Arrays.asList(declaredFields));
        if (!clazz.equals(Object.class)) {
            return getFieldByCurrentAndSuper(clazz.getSuperclass(), fields);
        }
        return fields;
    }

    /**
     * 根据 XFunction 获取属性名
     */
    private String getFieldName(XFunction<T, ?> fn) {
        // 从function取出序列化方法
        Method writeReplaceMethod;
        try {
            writeReplaceMethod = fn.getClass().getDeclaredMethod("writeReplace");
        } catch (NoSuchMethodException e) {
            throw new TreeUtilException(e);
        }

        // 从序列化方法取出序列化的lambda信息
        boolean isAccessible = writeReplaceMethod.isAccessible();
        writeReplaceMethod.setAccessible(true);
        SerializedLambda serializedLambda;
        try {
            serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(fn);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new TreeUtilException(e);
        }
        writeReplaceMethod.setAccessible(isAccessible);

        // 从lambda信息取出method、field、class等
        String implMethodName = serializedLambda.getImplMethodName();
        // 确保方法是符合规范的get方法，boolean类型是is开头
        if (!implMethodName.startsWith("is") && !implMethodName.startsWith("get")) {
            throw new TreeUtilException("get方法名称: " + implMethodName + ", 不符合java bean规范");
        }

        // get方法开头为 is 或者 get，将方法名 去除is或者get，然后首字母小写，就是属性名
        int prefixLen = implMethodName.startsWith("is") ? 2 : 3;

        String fieldName = implMethodName.substring(prefixLen);
        String firstChar = fieldName.substring(0, 1);
        fieldName = fieldName.replaceFirst(firstChar, firstChar.toLowerCase());
        return fieldName;
    }

    @FunctionalInterface
    public interface XFunction<T, R> extends Function<T, R>, Serializable {
    }

    @Data
    @AllArgsConstructor
    public class MapperParam {

        /**
         * 当前数据
         */
        private T current;

        /**
         * 父级数据
         */
        private T parent;
    }

}
